package com.googlecode.transmutant.spring;


import java.util.List;

import java.util.Map;

import javax.management.RuntimeErrorException;

import com.googlecode.transmutant.Steppable 
import org.springframework.beans.factory.annotation.Autowired 
import org.springframework.context.ApplicationContext 


class SpringStepper implements Steppable, GroovyInterceptable {
	
	StepsWrapper stepsWrapper
	Boolean autorun
	Captures captures = new Captures()
	
	@Autowired
	private ApplicationContext context
	
	void run() {
		def steps = stepsWrapper.createSteps()
		step(steps)
	}
	
	@Override
	public void step(Object steps) {
		captures.clear()
		
		for (StepWrapper step in steps) {
			runStep(step)
			getOuts(step)
			def capturesList = captures.captures.values();
			def capturesToRemove = []
			capturesList.each{Capture capture->
				if(!capture.retain) {
					capturesToRemove.add(capture)
				}
			}
			capturesToRemove.each {capture->
				captures.removeCapture(capture)
			}
		}
	}
	
	public Captures getCaptures() {
		return captures
	}
	
	void runStep(StepWrapper step) {
	}
	
	void getOuts(StepWrapper step) {
	}
	
	@Override
	def invokeMethod(String name, Object objs) {
		if(name.equals("runStep")) {
			StepWrapper wrapper = objs[0]
			
			if(wrapper.capture != null) {
				wrapper.ref = captures.getCapturedValue(wrapper.capture)
			}
			
			List<Capture> ins = wrapper.ins
			if(!(ins == null || ins.size() == 0)) {
				ins.each {capture->
					def actualStep = wrapper.ref
					if(actualStep.hasProperty(capture.property)) {
						actualStep.putAt(capture.property, captures.getCapturedValue(capture.name))
					}
				}
			}
			
			List<StepMethodWrapper> methodWrappers = wrapper.stepMethods
			Map<String,Object> outMap = new HashMap<String,Object>()
			for (StepMethodWrapper methodWrapper in methodWrappers) {
				List<Object> args = null
				if(methodWrapper.params.size != 0) {
					args = []
					for (param in methodWrapper.params) {
						if(param instanceof InParam) {
							Object captureValue = captures.getCapturedValue(param.name)
							args.add(captureValue)
							Capture capture = captures.getCapture(param.name)
							if(!param.retain) {
								capture.setRetain(false)
							}
						} else {
							def ref = context.getBean(param.bean)
							args.add(ref)
						}
					}
				}
				
				def out = wrapper.ref.invokeMethod(methodWrapper.name,args)
				if(methodWrapper.capture != null) {
					captures.capture(out, methodWrapper.capture)
				}
			}
			return outMap
		} else if (name.equals("getOuts")) {
			StepWrapper wrapper = objs[0]
			List<Capture> captureList = wrapper.captures
			captureList.each {capture->
				def value = wrapper.ref.getAt(capture.property)
				captures.capture(value, capture)
			}
		} else {
			def metaMethod = metaClass.getMetaMethod(name, objs)
			def result = metaMethod.invoke(this, objs)
			return result
		}
	}
}